package kubernetescli

import (
	"context"
	"errors"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/Kindling-project/chaos-backend/api"
	"github.com/Kindling-project/chaos-backend/tools/wrapper"
	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
)

var DefaultServicePortEntryMap *ServicePortEntryMap

func init() {
	DefaultServicePortEntryMap = NewServicePortEntryMap()
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-basic-service", 15680)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-travel-service", 12346)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-station-service", 18855)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-train-service", 14567)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-route-service", 11178)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-price-service", 16579)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-seat-service", 18898)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-order-service", 12031)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-config-service", 15679)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-travel-plan-service", 14322)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-route-plan-service", 14578)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-travel2-service", 16346)
	DefaultServicePortEntryMap.AddPortEntryForHTTPChaos("ts-order-other-service", 12032)
}

type httpchaos struct {
}

func (n *httpchaos) ToChaosVO(obj *interface{}) []api.Chaos {
	return wrapper.HTTPChaosToChaosVO(obj)
}

func (n *httpchaos) List() (interface{}, error) {
	if client == nil {
		return nil, errors.New("kube-client not initialized yet")
	}
	result := v1alpha1.HTTPChaosList{}
	err := client.
		Get().
		Resource("httpchaos").
		Namespace("train-ticket").
		Do(context.Background()).
		Into(&result)
	return result, err
}

func (n *httpchaos) Create(vo *api.Chaos) (interface{}, error) {
	if client == nil {
		return nil, errors.New("kube-client not initialized yet")
	}

	n.DeleteAll()

	// 根据传入的参数和默认参数生成CRD
	httpchaos := buildHTTPChaos(vo, defaultHTTPChaosParam)

	if httpchaos == nil {
		return nil, errors.New("unable to inject HTTP fault into this port")
	}

	// 通过client创建CRD
	result := v1alpha1.HTTPChaos{}
	err := client.
		Post().
		Namespace(httpchaos.Namespace).
		Resource("httpchaos").
		Body(httpchaos).
		Do(context.Background()).
		Into(&result)
	return result, err
}

func (n *httpchaos) DeleteAll() (interface{}, error) {
	if client == nil {
		return nil, errors.New("kube-client not initialized yet")
	}

	result := v1alpha1.HTTPChaosList{}
	err := client.
		Get().
		Resource("httpchaos").
		Namespace("train-ticket").
		Do(context.Background()).
		Into(&result)

	if err != nil {
		return nil, err
	}

	var lastChaos v1alpha1.HTTPChaos
	for _, chaos := range result.Items {
		if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result := v1alpha1.HTTPChaos{}
			err := client.
				Delete().
				Resource("httpchaos").
				Name(chaos.Name).
				Namespace(chaos.Namespace).
				Do(context.Background()).Into(&result)
			if err != nil {
				return nil, err
			}
			lastChaos = chaos
		}
	}

	return lastChaos, err
}

var err_code int32 = http.StatusNotFound

var defaultHTTPChaosParam = v1alpha1.HTTPChaos{
	Spec: v1alpha1.HTTPChaosSpec{
		PodHttpChaosActions: v1alpha1.PodHttpChaosActions{
			Replace: &v1alpha1.PodHttpChaosReplaceActions{
				Code: &err_code,
			},
		},
		Target: v1alpha1.PodHttpResponse,
	},
}

func buildHTTPChaos(vo *api.Chaos, params v1alpha1.HTTPChaos) *v1alpha1.HTTPChaos {
	var resource = params.DeepCopy()
	ts := time.Now().Unix()

	resource.Name = "httpchaos" + "-" + strconv.FormatInt(ts, 10)
	resource.Namespace = "train-ticket"
	// Mark labels
	resource.Labels = map[string]string{
		"kindling-chaos": "train-ticket",
	}
	resource.Annotations = map[string]string{
		"describe":       vo.Desc,
		"complex-action": vo.ComplexAction,
	}

	switch vo.ChaosAction {
	case "replace":
		resource.Spec.PodHttpChaosActions = defaultHTTPChaosParam.Spec.PodHttpChaosActions
		resource.Spec.Target = defaultHTTPChaosParam.Spec.Target
	}

	for i := 0; i < len(vo.Params); i++ {
		switch vo.Params[i].Name {
		case "instance":
			resource.Spec.Selector.LabelSelectors = map[string]string{
				"app": vo.Params[i].Value,
			}
			resource.Annotations["target"] = vo.Params[i].Value
			resource.Spec.Mode = v1alpha1.AllMode

			if entryData, ok := DefaultServicePortEntryMap.Load(vo.Params[i].Value); ok {
				resource.Spec.Port = entryData.(int32)
			} else {
				return nil
			}

		}
	}

	return resource
}

// ServicePortEntryMap 用于根据用户传入的服务名获取对应的服务端口
type ServicePortEntryMap struct {
	sync.Map
}

func NewServicePortEntryMap() *ServicePortEntryMap {
	return &ServicePortEntryMap{}
}

func (smm *ServicePortEntryMap) AddPortEntryForHTTPChaos(service string, port int32) {
	smm.Store(service, port)
}
